#ifdef USBCON
#include "ch32v_def.h"
#include "core_gcc.h"
#include "usbd_def.h"
#include "usbd_desc.h"

#if !USBD_PID && !USBD_VID
#undef USBD_PID
#undef USBD_VID

//  0x86,0x1a,      //厂商ID
//  0x22,0x57,      //产品设备ID

#define USBD_VID 0x1A86 //0x0483

#if defined(USBD_USE_HID_COMPOSITE)
#define USBD_PID 0x5711
#elif defined(USBD_USE_CDC)
#define USBD_PID 0x5722 
#endif

#endif /* !USBD_PID && !USBD_VID */

#if defined(USB_MANUFACTURER_STRING)
  #define USBD_MANUFACTURER_STRING USB_MANUFACTURER_STRING
#elif USBD_VID == 0x2341
  #define USBD_MANUFACTURER_STRING "Arduino LLC"
#elif USBD_VID == 0x2A03
  #define USBD_MANUFACTURER_STRING "Arduino srl"
#elif USBD_VID == 0x0483
  #define USBD_MANUFACTURER_STRING "STMicroelectronics"
#elif USBD_VID == 0x1A86
  #define USBD_MANUFACTURER_STRING "QinHeng Electronics"
#else
  #define USBD_MANUFACTURER_STRING "Unknown"
#endif

#define USBD_LANGID_STRING 0x409

#if defined(USB_PRODUCT_STRING)
  #define USBD_CLASS_PRODUCT_HS_STRING        USB_PRODUCT_STRING
  #define USBD_CLASS_PRODUCT_FS_STRING        USB_PRODUCT_STRING
#elif defined(USBD_USE_HID_COMPOSITE)
  #define USBD_CLASS_PRODUCT_HS_STRING        CONCATS(BOARD_NAME, "HID in HS Mode")
  #define USBD_CLASS_PRODUCT_FS_STRING        CONCATS(BOARD_NAME, "HID in FS Mode")
#elif defined(USBD_USE_CDC)
  #define USBD_CLASS_PRODUCT_HS_STRING        CONCATS(BOARD_NAME, "CDC in HS Mode")
  #define USBD_CLASS_PRODUCT_FS_STRING        CONCATS(BOARD_NAME, "CDC in FS Mode")
#else
  #define USBD_CLASS_PRODUCT_HS_STRING        CONCATS(BOARD_NAME, "in HS Mode")
  #define USBD_CLASS_PRODUCT_FS_STRING        CONCATS(BOARD_NAME, "in FS Mode")
#endif

#ifdef USBD_USE_HID_COMPOSITE
  #define USBD_CLASS_CONFIGURATION_HS_STRING  CONCATS(BOARD_NAME, "HID Config")
  #define USBD_CLASS_INTERFACE_HS_STRING      CONCATS(BOARD_NAME, "HID Interface")
  #define USBD_CLASS_CONFIGURATION_FS_STRING  CONCATS(BOARD_NAME, "HID Config")
  #define USBD_CLASS_INTERFACE_FS_STRING      CONCATS(BOARD_NAME, "HID Interface")
#endif /* USBD_USE_HID_COMPOSITE */

#ifdef USBD_USE_CDC
  #define USBD_CLASS_CONFIGURATION_HS_STRING  CONCATS(BOARD_NAME, "CDC Config")
  #define USBD_CLASS_INTERFACE_HS_STRING      CONCATS(BOARD_NAME, "CDC Interface")
  #define USBD_CLASS_CONFIGURATION_FS_STRING  CONCATS(BOARD_NAME, "CDC Config")
  #define USBD_CLASS_INTERFACE_FS_STRING      CONCATS(BOARD_NAME, "CDC Interface")
#endif /* USBD_USE_CDC */

#ifdef USBD_USE_HID_COMPOSITE

uint8_t USBD_Class_DeviceDesc[USB_LEN_DEV_DESC] = {
    0x12,                      //设备描述符长度，18字节
    USB_DESC_TYPE_DEVICE,      //描述符类型，0x01为设备描述符
    0x00,                      //本设备所使用USB版本协议，因为是小端结构，所以低字节在前，
    0x02,                      //即USB1.1版本为0x10,0x01，USB2.0为0x00,0x02
    0x02,                      //类代码，CDC类为0x02，CDC类必须在设备描述符中指定设备类型，即0x02
    0x00,                      //子类代码，当类代码bDeviceClass为0时，下面的子类代码bDeviceSubClass也必须为0。
    0x00,                      //设备所使用的协议，协议代码由USB协会规定。当该字段为0时，表示设备不使用类所定义的协议。
    USB_MAX_EP0_SIZE,          //端点0的最大包长，可以取值8、16、32、64，此处为64字节
    LOBYTE(USBD_VID),          //厂商ID
    HIBYTE(USBD_VID),          //厂商ID
    LOBYTE(USBD_PID),          //产品设备ID
    HIBYTE(USBD_PID),          //产品设备ID
    0x00,                      //设备版本号
    0x01,                      //设备版本号
    USBD_IDX_MFC_STR,          //描述厂商的字符串索引值。当该值为0时，表示没有厂商字符串
    USBD_IDX_PRODUCT_STR,      //描述产品的字符串索引值。当该值为0时，表示没有产品字符串
    USBD_IDX_SERIAL_STR,       //描述设备的序列号字符串索引值。当该值为0时，表示没有序列号字符串
    USBD_MAX_NUM_CONFIGURATION //可能的配置数，通常为1
};

#endif // !USBD_USE_HID_COMPOSITE

#ifdef USBD_USE_CDC

uint8_t USBD_Class_DeviceDesc[USB_LEN_DEV_DESC] = {
    0x12,                      //设备描述符长度，18字节
    USB_DESC_TYPE_DEVICE,      //描述符类型，0x01为设备描述符
    0x00,                      //本设备所使用USB版本协议，因为是小端结构，所以低字节在前，
    0x02,                      //即USB1.1版本为0x10,0x01，USB2.0为0x00,0x02
    0x02,                      //类代码，CDC类为0x02，CDC类必须在设备描述符中指定设备类型，即0x02
    0x00,                      //子类代码，当类代码bDeviceClass为0时，下面的子类代码bDeviceSubClass也必须为0。
    0x00,                      //设备所使用的协议，协议代码由USB协会规定。当该字段为0时，表示设备不使用类所定义的协议。
    USB_MAX_EP0_SIZE,          //端点0的最大包长，可以取值8、16、32、64，此处为64字节
    LOBYTE(USBD_VID),          //厂商ID
    HIBYTE(USBD_VID),          //厂商ID
    LOBYTE(USBD_PID),          //产品设备ID
    HIBYTE(USBD_PID),          //产品设备ID
    0x00,                      //设备版本号
    0x01,                      //设备版本号
    USBD_IDX_MFC_STR,          //描述厂商的字符串索引值。当该值为0时，表示没有厂商字符串
    USBD_IDX_PRODUCT_STR,      //描述产品的字符串索引值。当该值为0时，表示没有产品字符串
    USBD_IDX_SERIAL_STR,       //描述设备的序列号字符串索引值。当该值为0时，表示没有序列号字符串
    USBD_MAX_NUM_CONFIGURATION //可能的配置数，通常为1
};

#endif // !USBD_USE_CDC

uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] = {
    USB_LEN_LANGID_STR_DESC,
    USB_DESC_TYPE_STRING,
    LOBYTE(USBD_LANGID_STRING),
    HIBYTE(USBD_LANGID_STRING),
};

uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] = {
  USB_SIZ_STRING_SERIAL,
  USB_DESC_TYPE_STRING,
};

uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ];

static inline void IntToUnicode(uint32_t value, uint8_t *pbuf, uint8_t len)
{
    uint8_t idx = 0U;

    for (idx = 0U ; idx < len ; idx ++) {
      if (((value >> 28)) < 0xAU) {
        pbuf[ 2U * idx] = (value >> 28) + '0';
      } else {
        pbuf[2U * idx] = (value >> 28) + 'A' - 10U;
      }

      value = value << 4;

      pbuf[2U * idx + 1] = 0U;
    }
}

static inline void Get_SerialNum(void)
{
    uint32_t deviceserial0, deviceserial1, deviceserial2;

    deviceserial0 = *(uint32_t *)DEVICE_ID1;
    deviceserial1 = *(uint32_t *)DEVICE_ID2;
    deviceserial2 = *(uint32_t *)DEVICE_ID3;

    deviceserial0 += deviceserial2;

    if (deviceserial0 != 0U) {
        IntToUnicode(deviceserial0, &USBD_StringSerial[2], 8U);
        IntToUnicode(deviceserial1, &USBD_StringSerial[18], 4U);
    }
}

uint8_t *USBD_LangIDStrDescriptor(uint16_t *length)
{
    *length = sizeof(USBD_LangIDDesc);
    return (uint8_t *)USBD_LangIDDesc;
}

uint8_t *USBD_ManufacturerStrDescriptor(uint16_t *length)
{
    USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);
    return USBD_StrDesc;
}

uint8_t* USBD_SerialStrDescriptor(uint16_t *length) 
{
    *length = USB_SIZ_STRING_SERIAL;
    Get_SerialNum();
    return (uint8_t *)USBD_StringSerial;
}

uint8_t* USBD_Class_DeviceDescriptor(uint16_t *length) 
{
    *length = sizeof(USBD_Class_DeviceDesc);
    return (uint8_t *)USBD_Class_DeviceDesc;
}

uint8_t* USBD_Class_ProductStrDescriptor(uint16_t *length) 
{
    USBD_GetString((uint8_t *)USBD_CLASS_PRODUCT_FS_STRING, USBD_StrDesc, length);
    return USBD_StrDesc;
}

uint8_t* USBD_Class_ConfigStrDescriptor(uint16_t *length) 
{
    USBD_GetString((uint8_t *)USBD_CLASS_CONFIGURATION_FS_STRING, USBD_StrDesc, length);
    return USBD_StrDesc;
}

uint8_t* USBD_Class_InterfaceStrDescriptor(uint16_t *length) 
{
    USBD_GetString((uint8_t *)USBD_CLASS_INTERFACE_FS_STRING, USBD_StrDesc, length);
    return USBD_StrDesc;
}

#endif